/*
 *
 * This is a VoIP Application designed for local-network usage for voice and
 * messaging
 * Copyright (C) 2012  Imran M Yousuf (imyousuf@smitsol.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.smitsol.voip.server.sip;

import com.smitsol.voip.api.API;
import com.smitsol.voip.api.Configurations;
import com.smitsol.voip.api.domains.User;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ListIterator;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author imyousuf
 */
public class SipAuthenticator {

  private MessageDigest md = null;
  private SecureRandom random = null;
  private final String realm;
  private final API api;
  protected final transient Logger logger = LoggerFactory.getLogger(getClass());

  public SipAuthenticator(API api) {
    this.api = api;
    this.realm = api.getConfig(Configurations.AUTH_REALM);
    try {
      md = MessageDigest.getInstance("MD5");
      random = SecureRandom.getInstance("SHA1PRNG");
      random.setSeed(System.currentTimeMillis());
    }
    catch (NoSuchAlgorithmException algEx) {
    }
  }

  public String isAuthentic(ListIterator authHeaderVals, String uri, String method) {
    if (authHeaderVals != null && authHeaderVals.hasNext()) {
      // validate
      String qop = "auth";
      String username = "";
      String password = "";
      String nonce = "";
      String cnonce = "";
      String nc = "";
      String response = "";

      while (authHeaderVals.hasNext()) {
        String authHeader = authHeaderVals.next().toString();

        StringTokenizer comaTok = new StringTokenizer(authHeader, ",");
        while (comaTok.hasMoreTokens()) {
          String token = comaTok.nextToken().trim();
          int equals = token.indexOf("=");
          String param = token.substring(0, equals);
          String value = token.substring(equals + 1).trim();
          if (logger.isDebugEnabled()) {
            logger.debug("AA Param : " + param + ":" + value);
          }
          if (value.indexOf("\"") == 0 &&
              value.lastIndexOf("\"") == (value.length() - 1)) {
            value = value.substring(1, value.length() - 1);
          }
          if (param.equalsIgnoreCase("response")) {
            response = value;
          }
          else if (param.equalsIgnoreCase("nc")) {
            nc = value;
          }
          else if (param.equalsIgnoreCase("cnonce")) {
            cnonce = value;
          }
          else if (param.equalsIgnoreCase("nonce")) {
            nonce = value;
          }
          else if (param.equalsIgnoreCase("digest username")) {
            username = value;
            // Load User
            User user = api.getUserService().getUserByUsername(username);
            if (user == null) {
              return null;
            }
            password = user.getPassword();
          }
        }
      }
      // calculate
      StringBuilder h1 = new StringBuilder();
      h1.append(username).append(':').append(realm).append(':').append(
          password);

      StringBuilder h2 = new StringBuilder();
      h2.append(method).append(':').append(uri);
      try {
        byte[] H1 = md.digest(h1.toString().getBytes("US-ASCII"));
        byte[] H2 = md.digest(h2.toString().getBytes("US-ASCII"));

        StringBuilder sb = new StringBuilder();
        sb.append(toHex(H1)).append(':').append(nonce).append(':').append(
            nc).append(':').append(cnonce).append(':').append(qop).append(':').append(toHex(H2));

        byte[] genResp = md.digest(sb.toString().getBytes("US-ASCII"));
        String asciiResp = toHex(genResp);
        if (logger.isDebugEnabled()) {
          logger.debug("Generated Response = " + asciiResp);
          logger.debug("Provided Response  = " + response);
        }
        return username;
      }
      catch (Exception ex) {
        logger.error("Error authenticating!", ex);
      }
    }
    return null;
  }

  public String getAuntehicationHeaderValue() {
    // generate challange
    StringBuilder sb = new StringBuilder();
    byte[] temp = new byte[40];
    random.nextBytes(temp);
    for (int i = 0; i < temp.length; i++) {
      String d = Integer.toHexString(new Byte(temp[i]).intValue() & 0xFF);
      if (d.length() == 1) {
        sb.append('0');
      }
      sb.append(d);
    }
    String nonce = sb.toString();
    String opaque = "";

    StringBuilder wwwAuth = new StringBuilder("Digest realm=\"");
    wwwAuth.append(realm).append("\", qop=\"auth\", nonce=\"");
    wwwAuth.append(nonce).append("\", opaque=\"");
    wwwAuth.append(opaque).append("\", stale=FALSE, algorithm=MD5");
    return wwwAuth.toString();
  }

  public static String toHex(byte[] input) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < input.length; i++) {
      String d = Integer.toHexString(new Byte(input[i]).intValue() & 0xFF);
      if (d.length() == 1) {
        sb.append('0');
      }
      sb.append(d);
    }
    return sb.toString();
  }
}